import axios from 'axios';
import apiConfig from '@/api.config';
import { addPending, removePending } from './pending';
import _merge from 'lodash/merge';
import validator from './validator';
import { showModal } from './validator/helper';
import $loading from '../loading';

import whiteList, { reverse } from '../../../mock/whiteList';
import { TOKEN } from '@/utils/storageKey';
const _print = (config: IAxiosRequestConfig, data: any, error: any = '') => {
  const _action =
    config.url?.indexOf('/') === 0 ? config.url.substring(1) : `${config.url}`;
  const { headers, branch } = config;

  console.group(
    `%c${config.mock ? '【mock】' : ''}${branch}.Action：`,
    'background:#00b38a;color:#fff',
    _action || `sid：${headers.sid || ''}`
  );

  branch !== 'node' &&
    console.log(
      '%cURL：',
      'color:#00b38a',
      `${
        process.env.NODE_ENV === 'production'
          ? apiConfig(process.env.MY_ENV, config.branch)
          : window.location.origin
      }/${_action}`
    );

  console.log('%cMethod：', 'color:#00b38a', config.method);

  console.log('%cIn-Params：', 'color:#00b38a', config.params || config.data);
  console.log('%cOut-Params：', 'color:#00b38a', data);
  error !== '' && console.log('%cError：', 'color:#ff0000', error);
  console.groupEnd();
};

const _undefinedOrTrueIsTrue = (val?: boolean) => {
  return val === undefined || val ? true : false;
};

const _matchRes = (url: string) => {
  const res = (whiteList as string[]).includes(url);
  return reverse ? res : !res;
};

export const _request = (config: IAxiosRequestConfig) => {
  config.redirectToLogin = _undefinedOrTrueIsTrue(config.redirectToLogin);
  config.codeErrTip = _undefinedOrTrueIsTrue(config.codeErrTip);
  config.rejectErrTip = _undefinedOrTrueIsTrue(config.rejectErrTip);
  // config.loading = _undefinedOrTrueIsTrue(config.loading);
  // config.cancel = _undefinedOrTrueIsTrue(config.cancel);
  config.branch = config.branch || 'master';

  let baseURL = '';

  // 开发模式开启代理，nodeAPI 默认已经开启跨域，无需代理
  if (process.env.NODE_ENV !== 'production' && config.branch !== 'node') {
    // 如果白名单数组有元素，就针对白名单数组里的 api 进行 mock
    if (process.env.MY_MOCK === 'true') {
      // 判断当前是否mock，判断白名单的数组是否有数据
      if (whiteList.length > 0 && _matchRes(config.url!)) {
        config.url = `/${config.branch}/${config.url}`;
      } else {
        config.mock = true;
      }
    } else {
      config.url = `/${config.branch}/${config.url}`;
    }
  } else {
    if (process.env.MY_MOCK === 'true' && config.branch !== 'node') {
      if (whiteList.length > 0 && _matchRes(config.url!)) {
        baseURL = apiConfig(process.env.MY_ENV, config.branch);
      } else {
        config.mock = true;
      }
    } else {
      baseURL = apiConfig(process.env.MY_ENV, config.branch);
    }
  }

  // 在请求开始前，对之前的请求做检查取消操作
  config.cancel && removePending(config);
  // 将当前请求添加到 pending 中
  config.cancel && addPending(config);

  config.loading && $loading.show();

  return axios
    .request(
      _merge(
        {
          timeout: 20000,
          headers: {
            'Content-Type': 'application/json',
            authorization: localStorage.getItem(TOKEN)
          }
        },
        { baseURL },
        config
      )
    )
    .then(res => {
      const data = res.data;

      // 在请求结束后，移除本次请求
      config.cancel && removePending(res.config);

      if (data && res.status === 200) {
        // 开始验证，错误将全局提示
        validator.start(config.branch!, data, config);

        _print(config, data);
        config.loading && $loading.hide();
        return data;
      }
      return Promise.reject(new Error('Response Error'));
    })
    .catch(error => {
      const retData = {
        code: 4400,
        data: null,
        message: 'catch error',
        error
      };

      _print(config, retData, error);
      config.loading && $loading.hide();

      // 判断当前接口是否是被取消的
      if (axios.isCancel(error)) {
        console.log('Request canceled', error.message);
      } else if (error.message) {
        if (error.message.indexOf('timeout') !== -1) {
          const message = '请求超时，请稍后再试';
          config.rejectErrTip && showModal(message);
        } else if (error.message.indexOf('Network Error') !== -1) {
          const message = '您的网络好像断开了哦~';
          config.rejectErrTip && showModal(message);
        } else if (error.message.indexOf('Response Error') !== -1) {
          const message = '请求返回时发生错误';
          config.rejectErrTip && showModal(message);
        } else {
          const message = '服务器返回失败/请求接口错误';
          config.rejectErrTip && showModal(message);
        }
      }
      return Promise.resolve(retData);
    });
};

/**
 * @desc 请求方法
 */
class Request {
  private extends: any;
  constructor() {
    this.extends = [];
  }
  extend(extend: any) {
    this.extends.push(extend);
    return this;
  }
  merge() {
    const obj = this.extends.reduce((prev: any, curr: any) => {
      return _merge(prev, curr);
    }, {});
    Object.keys(obj).forEach(key => {
      Object.assign((this as any)[key], obj[key]);
    });
  }

  get(path: string, data: object = {}, config: IAxiosRequestConfig = {}) {
    return _request({
      ...config,
      method: 'GET',
      url: path,
      params: data
    });
  }
  post(path: string, data: object = {}, config: IAxiosRequestConfig = {}) {
    return _request({
      ...config,
      method: 'POST',
      url: path,
      data
    });
  }
  delete(path: string, data: object = {}, config: IAxiosRequestConfig) {
    return _request({
      ...config,
      method: 'DELETE',
      url: path,
      params: data
    });
  }
  put(path: string, data: object = {}, config: IAxiosRequestConfig) {
    return _request({
      ...config,
      method: 'PUT',
      url: path,
      data
    });
  }
  upload(
    path: string,
    data: Record<string, any> = {},
    config: IAxiosRequestConfig
  ) {
    const formData: FormData = new FormData();
    for (const key in data) {
      formData.append(key, data[key]);
    }
    return _request({
      ...config,
      method: 'POST',
      url: path,
      data: formData
    });
  }
}

export default Request;
